/**
 * 
 */
package it.unibo.refolding.pico;

import it.unibo.refolding.common.CommonThings;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import it.unimi.dsi.fastutil.doubles.DoubleList;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.builder.CompareToBuilder;

import com.google.common.base.Preconditions;

public class RetractionPeak {
	public final double contourLengthM;
	public final double startForceN;
	public final double ruptureForceN;
	public final double extensionM;

	public static final Comparator<RetractionPeak> CONTOUR_LENGTH_COMPARATOR = new Comparator<RetractionPeak>() {
		@Override
		public int compare(RetractionPeak o1, RetractionPeak o2) {
			return new CompareToBuilder().append(o1.contourLengthM, o2.contourLengthM).append(
					o1.startForceN, o2.startForceN).append(o1.ruptureForceN, o2.ruptureForceN)
					.append(o1.extensionM, o2.extensionM)
					.toComparison();
		}
	};

	public RetractionPeak(double contourLengthM, double startForceN, double ruptureForceN, double extensionM) {
		this.contourLengthM = contourLengthM;
		Preconditions.checkArgument(0 <= startForceN);
		Preconditions.checkArgument(startForceN < ruptureForceN);
		this.startForceN = startForceN;
		this.ruptureForceN = ruptureForceN;
		// extensionM can also be 0, when reading in an old file format.
		Preconditions.checkArgument(extensionM >= 0);
		// extensionM will be higher than contourLengthM for low spring constants or high forces. 
		this.extensionM = extensionM;
	}

	public static DoubleList contourLengths(Iterable<RetractionPeak> peaks) {
		DoubleList cl = new DoubleArrayList();
		for (RetractionPeak peak : peaks) {
			cl.add(peak.contourLengthM);
		}
		return cl;
	}
	
	@Override
	public int hashCode() {
		return Objects.hash(contourLengthM, startForceN, ruptureForceN, extensionM);
	}

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof RetractionPeak)) {
            return false;
        }
        RetractionPeak other = (RetractionPeak) obj;
        return Double.doubleToLongBits(contourLengthM) == Double
            .doubleToLongBits(other.contourLengthM)
            & Double.doubleToLongBits(startForceN) == Double.doubleToLongBits(other.startForceN)
            & Double.doubleToLongBits(ruptureForceN) == Double
                .doubleToLongBits(other.ruptureForceN)
            & Double.doubleToLongBits(extensionM) == Double.doubleToLongBits(other.extensionM);
    }

	@Override
	public String toString() {
		return repr();
	}

	public String repr() {
		return String.format(Locale.ROOT, "RetractionPeak(%s, %s, %s, %s)", Double
				.toString(contourLengthM), Double.toString(startForceN), Double
				.toString(ruptureForceN), Double.toString(extensionM));
	}

	private static final Pattern peakPattern = Pattern.compile(String.format(
			"RetractionPeak\\((%s), (%s), (%s)(?:, (%s))?\\)", CommonThings.FP_REGEX,
			CommonThings.FP_REGEX, CommonThings.FP_REGEX, CommonThings.FP_REGEX));

	public static RetractionPeak parseRepr(String s) {
		Matcher matcher = peakPattern.matcher(s);
		boolean match = matcher.matches();
		Preconditions.checkArgument(match, "%s did not match regex %s", s, peakPattern);
		return new RetractionPeak(Double.parseDouble(matcher.group(1)), Double.parseDouble(matcher
				.group(2)), Double.parseDouble(matcher.group(3)),
				matcher.group(4) == null ? 0 : Double.parseDouble(matcher.group(4)));
	}

	public static String repr(Iterable<RetractionPeak> peaks) {
		StringBuilder sb = new StringBuilder();
		sb.append("[");
		Iterator<RetractionPeak> it = peaks.iterator();
		while (it.hasNext()) {
			RetractionPeak peak = it.next();
			sb.append(peak.repr());
			if (it.hasNext()) {
				sb.append(", ");
			}
		}
		sb.append("]");
		return sb.toString();
	}

	public static List<RetractionPeak> parseListRepr(String s) {
		String[] s2 = CommonThings.parseArrayRepr(s, "RetractionPeak");
		RetractionPeak[] peaks = new RetractionPeak[s2.length];
		for (int i = peaks.length; --i >= 0;) {
			String r = (i == 0 ? "" : "RetractionPeak") + s2[i];
			peaks[i] = parseRepr(r);
		}
		return Collections.unmodifiableList(Arrays.asList(peaks));
	}
}
